package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.github.pagehelper.util.StringUtil;
import com.zzyl.constant.CacheConstant;
import com.zzyl.constant.SuperConstant;
import com.zzyl.dto.ResourceDto;
import com.zzyl.entity.Resource;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.ResourceMapper;
import com.zzyl.mapper.RoleResourceMapper;
import com.zzyl.service.ResourceService;
import com.zzyl.utils.EmptyUtil;
import com.zzyl.utils.NoProcessing;
import com.zzyl.vo.*;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ResourceServiceImpl implements ResourceService {

    @Autowired
    private ResourceMapper resourceMapper;
    @Autowired
    private RoleResourceMapper roleResourceMapper;

    /*
     * 多条件查询资源列表
     */
    @Cacheable(value = CacheConstant.RESOURCE_LIST,key="#resourceDto.hashCode()")
    @Override
    public List<ResourceVo> findResourceList(ResourceDto resourceDto) {
        List<Resource> resourcesList = resourceMapper.selectList(resourceDto);
        return BeanUtil.copyToList(resourcesList, ResourceVo.class);
    }

    /*
     * 返回资源树形结构
     */
    @Cacheable(value = CacheConstant.RESOURCE_TREE)
    @Override
    public TreeVo resourceTreeVo(ResourceDto resourceDto) {
        //前端传递的参数没有用，在这里需要考虑隐藏因素
        ResourceDto dto = ResourceDto.builder()
                .dataState(SuperConstant.DATA_STATE_0)//查询的必须是启用的
                .parentResourceNo(NoProcessing.processString(SuperConstant.ROOT_PARENT_ID))//树根结构，即获取到所有的分支结构
                .resourceType(SuperConstant.MENU)//查询的必须是菜单，不能是按钮
                .build();
        //查询所有资源数据
        List<Resource> resourceList = resourceMapper.selectList(dto);
        if(EmptyUtil.isNullOrEmpty(resourceList)){
            throw new RuntimeException("资源信息未定义");
        }

        //数据库表没有根节点，所以查询回来的数据没有根节点，需要手动构建根节点
        Resource rootResource= new Resource();
        rootResource.setResourceNo(SuperConstant.ROOT_PARENT_ID);
        rootResource.setResourceName("智慧养老院");

        //返回树形集合
        List<TreeItemVo> itemVos=new ArrayList<>();

        //使用递归构建树形结构
        recursionTreeItem(itemVos,rootResource,resourceList);

        return TreeVo.builder()
                .items(itemVos)//给属性赋值
                .build();
    }

    /*
    * 递归方法构建树形结构
    */
    private void recursionTreeItem(List<TreeItemVo> itemVos, Resource rootResource, List<Resource> resourceList) {
        TreeItemVo treeItemVo =TreeItemVo.builder()//TreeVO对象items集合属性对应的对象
                .id(rootResource.getResourceNo())//根节点id
                .label(rootResource.getResourceName())//根节点名称
                .build();

        //获取当前资源下所有的子资源
        List<Resource> childrenResourceList = resourceList.stream()//转化所有查询到的为stream流
                .filter(n -> n.getParentResourceNo().equals(rootResource.getResourceNo()))//筛选出根节点下的子节点
                .collect(Collectors.toList());//收集这些子节点作为一个集合

        //判断子资源是否为空
        if(!EmptyUtil.isNullOrEmpty(childrenResourceList)){
            List<TreeItemVo> listChildren=new ArrayList<>();
            //构建子资源
            childrenResourceList.forEach(resource->{
                recursionTreeItem(listChildren,resource,resourceList);
            });
            treeItemVo.setChildren(listChildren);//此处是将树形结构添加到对象的集合属性当中
        }

        //将对象添加到集合中
        itemVos.add(treeItemVo);
    }

    /*
     * 添加资源
     */
    @Caching(evict = {
            @CacheEvict(value = CacheConstant.RESOURCE_TREE,allEntries = true),//清空树形结构
            @CacheEvict(value = CacheConstant.RESOURCE_LIST,allEntries = true)//清空资源列表
    })
    @Override
    public void createResource(ResourceDto resourceDto) {
        //属性拷贝
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);

        //查询父资源，并设置子资源的状态与父资源一样
        Resource parentResoutrce = resourceMapper.selectByResourceNo(resourceDto.getParentResourceNo());
        resource.setDataState(parentResoutrce.getDataState());

        boolean isIgnore=true;
        if(StringUtil.isNotEmpty(resourceDto.getResourceType())&& resourceDto.getResourceType().equals(SuperConstant.BUTTON)){
            isIgnore=false;
        }

        //创建当前资源编号
        String resourceNo=createResourceNo(resourceDto.getParentResourceNo(),isIgnore);
        resource.setResourceNo(resourceNo);

        //插入
        resourceMapper.insert(resource);
    }

    /*
     * 创建资源编号
     */
    private String createResourceNo(String parentResourceNo, boolean isIgnore) {
        //按钮不会进入这个操作
        //菜单超过三级不许创建子菜单
        if(isIgnore&&NoProcessing.processString(parentResourceNo).length()/3>=5){
            throw new BaseException(BasicEnum.RESOURCE_DEPTH_UPPER_LIMIT);
        }

        //根据父资源编号查询子资源编号
        ResourceDto dto = ResourceDto.builder().parentResourceNo(parentResourceNo).build();
        List<Resource> resourceList = resourceMapper.selectList(dto);
        if(EmptyUtil.isNullOrEmpty(resourceList)){
            //无下属节点，创建新的节点编号
            //100 001 001 000 000 —> 100 001 001 001 000（新建的）
            return NoProcessing.createNo(parentResourceNo, false);
        }else{
            //有下属节点，在已有的节点上追加
            //100 001 001 000 000 —> 100 001 001 001 000 ——> 100 001 001 002 000（新建的）
            //先获取已有节点最大的值
            Long maxNo = resourceList.stream().map(resource -> {
                return Long.valueOf(resource.getResourceNo());
            }).max(Comparator.comparing(i -> i)).get();

            //创建新的节点编号
            return NoProcessing.createNo(String.valueOf(maxNo), true);
        }

    }

    /**
     * 修改资源
     * @param resourceDto
     */
    @Caching(evict = {
            @CacheEvict(value = CacheConstant.RESOURCE_TREE,allEntries = true),//清空树形结构
            @CacheEvict(value = CacheConstant.RESOURCE_LIST,allEntries = true)//清空资源列表
    })
    @Override
    public void updateResource(ResourceDto resourceDto) {
        //转换
        Resource resource = BeanUtil.toBean(resourceDto, Resource.class);
        int flag = resourceMapper.updateByPrimaryKeySelective(resource);
        if (flag == 0) {
            throw new RuntimeException("修改资源信息出错");
        }
    }

    /**
     * 启用禁用
     * @param resourceDto
     * @return
     */
    @Override
    public void isEnable(ResourceDto resourceDto) {
        //启用菜单
        if (resourceDto.getDataState().equals("0")) {
            //判断父级菜单是否是禁用，如果是禁用，不允许启用
            String parentResourceNo = resourceDto.getParentResourceNo();
            Resource parentResource = resourceMapper.selectByResourceNo(parentResourceNo);
            if (parentResource != null && parentResource.getDataState().equals("1")) {
                throw new BaseException(BasicEnum.PARENT_MENU_DISABLE);
            }
        }
        //TODO 当按钮被分配的时候，出错误提示：按钮已分配，不允许禁用   已完成
        int count = roleResourceMapper.checkMenuExistRole(resourceDto.getResourceNo());
        if (resourceDto.getDataState().equals("1") && count > 0) {
            Resource resource = resourceMapper.selectByResourceNo(resourceDto.getResourceNo());
            if(resource.getResourceType().equals("m")){
                throw new BaseException(BasicEnum.MENU_DISTRIBUTED);
            }else {
                throw new BaseException(BasicEnum.BUTTON_DISTRIBUTED);
            }
        }


        //判断是否有子菜单，如果有子菜单，则一起启用或禁用
        String resourceNo = resourceDto.getResourceNo();
        resourceMapper.updateByResourceNo(resourceNo, resourceDto.getDataState());
        resourceMapper.updateByParentResourceNo(NoProcessing.processString(resourceNo), resourceDto.getDataState());
    }

    /*
    * 删除菜单
    */
    @Caching(evict = {
            @CacheEvict(value = CacheConstant.RESOURCE_TREE,allEntries = true),//清空树形结构
            @CacheEvict(value = CacheConstant.RESOURCE_LIST,allEntries = true)//清空资源列表
    })
    @Override
    public void deleteByResourceNo(String resourceNo) {
        if (hasChildByMenuId(resourceNo)) {
            throw new RuntimeException("存在子菜单,不允许删除");
        }

        //TODO 如果当前菜单被角色引用，则不能删除 已完成
        if (checkMenuExistRole(resourceNo)) {
            throw new RuntimeException("菜单已分配,不允许删除");
        }

        Resource resource = resourceMapper.selectByResourceNo(resourceNo);
        if(resource == null || resource.getDataState().equals(SuperConstant.DATA_STATE_0)){
            throw new RuntimeException("菜单启用状态,不能删除");
        }

        resourceMapper.deleteByResourceNo(resourceNo);
    }

    /**
     * 查询菜单使用数量
     * @param resourceNo 菜单编号
     * @return 结果
     */
    public boolean checkMenuExistRole(String resourceNo) {
        int result = roleResourceMapper.checkMenuExistRole(resourceNo);
        return result > 0 ? true : false;
    }

    /**
     * 判断是否有子菜单
     * @param resourceNo
     * @return
     */
    private boolean hasChildByMenuId(String resourceNo) {
        int result = resourceMapper.hasChildByMenuId(resourceNo);
        return result > 0 ? true : false;
    }

    /*
     * 左侧菜单
     */
    @Override
    public List<MenuVo> menus(Long userId) {
        //查询用户对应的所有菜单数据
        List<MenuVo> menuVoList = resourceMapper.findListByUserId(userId);
        if(CollUtil.isEmpty(menuVoList)){
            throw new BaseException(BasicEnum.USER_ROLE_AND_MENU_EMPTY);
        }
        //数据分组根据父级菜单编号————key：父资源编号，value：当前父资源下一级的子资源菜单
        Map<String, List<MenuVo>> parentRNoMap = menuVoList.stream()
                .collect(Collectors.groupingBy(MenuVo::getParentResourceNo));

        //遍历数据，为用户对应的所有菜单（每一个添加往下一级的子资源）
        menuVoList.forEach(menuVo -> {
            menuVo.setMeta(MenuMetaVo.builder().title(menuVo.getName()).build());//菜单名字
            menuVo.setRedirect("/"+menuVo.getName());
            //根据父编号到map集合中找是否包含子菜单，如果包含则设置为当前菜单的子菜单
            List<MenuVo> menuVos = parentRNoMap.get(menuVo.getResourceNo());
            //如果子菜单不为空
            if(!EmptyUtil.isNullOrEmpty(menuVos)){
                menuVos.forEach(menuVo1 -> {
                    menuVo1.setMeta(MenuMetaVo.builder().title(menuVo1.getName()).build());
                    menuVo1.setRedirect("/"+menuVo1.getName());
                });
                menuVo.setChildren(menuVos);
            }
        });

        //上述循环完毕后，所有值均已封装完毕，只需要返回根编号即可
        return parentRNoMap.get(SuperConstant.ROOT_PARENT_ID);
    }

}
